#include "DMI.h"
#include "Tech.h"
#include <math.h>
#include "../FacilityBaseLib/KData.h"
//////////////////////////////////////////////////////////////////////
//	CDMI
CDMI::CDMI( )
{
	SetDefaultParameters( );
	m_pdDMICache		=	NULL;
	m_pnDMICacheIndex	=	NULL;
	m_nDMICacheCurrent	=	-1;
}

CDMI::CDMI( KdataContainer * pKData )
: TechnicalIndicator( pKData )
{
	SetDefaultParameters( );
	m_pdDMICache		=	NULL;
	m_pnDMICacheIndex	=	NULL;
	m_nDMICacheCurrent	=	-1;
}

CDMI::~CDMI()
{
	clear( );
}

void CDMI::SetDefaultParameters( )
{
	m_nDays			=	7;
}

void CDMI::attach( CDMI & src )
{
	m_nDays			=	src.m_nDays;
}

bool CDMI::IsValidParameters( )
{
	return ( VALID_DAYS(m_nDays) );
}

void CDMI::clear( )
{
	TechnicalIndicator::clear( );

	if( m_pdDMICache )
	{
		delete	[]	m_pdDMICache;
		m_pdDMICache	=	NULL;
	}
	if( m_pnDMICacheIndex )
	{
		delete	[]	m_pnDMICacheIndex;
		m_pnDMICacheIndex	=	NULL;
	}
	m_nDMICacheCurrent	=	-1;
}


bool CDMI::CalculateDM(double *pDMPlus, double *pDMMinus, double *pTR, size_t nIndex)
{
	STT_ASSERT_CALCULATE1( m_pKData, nIndex );

	if( nIndex < 1 )
		return false;

	double	dDMPlus = 0, dDMMinus = 0, dTR = 0;
	KDATA	kdNow	=	m_pKData->at(nIndex);
	KDATA	kdLast	=	m_pKData->at(nIndex-1);

	dDMPlus		=	max( 0.0, ((double)kdNow.HighestPrice) - kdLast.HighestPrice );
	dDMMinus	=	max( 0.0, ((double)kdLast.LowestPrice) - kdNow.LowestPrice );

	dTR	=	max( fabs(((double)kdNow.ClosePrice)-kdNow.LowestPrice), fabs(((double)kdNow.HighestPrice)-kdLast.ClosePrice) );
	dTR	=	max( dTR, fabs(((double)kdNow.LowestPrice)-kdLast.ClosePrice) );

	if( pDMPlus )	*pDMPlus	=	dDMPlus;
	if( pDMMinus )	*pDMMinus	=	dDMMinus;
	if( pTR )		*pTR		=	dTR;
	return true;
}

bool CDMI::CalculateDIDX(double *pDIPlus, double *pDIMinus, double *pDX, size_t nIndex, size_t nDays)
{
	STT_ASSERT_CALCULATE( m_pKData, nIndex, nDays );

	if( m_nDays > nIndex )
		return false;

	double	dDX, dADX = 0;
	double	dDIPlus = 0, dDIMinus = 0, dTRSum = 0;
	int	nCount	=	0;
	for( int k=nIndex; k>=1; k-- )
	{
		double	dDMPlus = 0, dDMMinus = 0, dTR = 0;
		if( !CalculateDM( &dDMPlus, &dDMMinus, &dTR, k ) )
			return false;

		dDIPlus		+=	dDMPlus;
		dDIMinus	+=	dDMMinus;
		dTRSum		+=	dTR;

		nCount	++;
		if( nCount == nDays )
		{
			if( fabs(dTRSum) < 1e-4 || fabs(dDIPlus+dDIMinus) < 1e-4 )
				return false;
			dDIPlus		=	(dDIPlus * 100/dTRSum);
			dDIMinus	=	(dDIMinus * 100/dTRSum);
			dDX	=	fabs(dDIPlus-dDIMinus) * 100 / (dDIPlus+dDIMinus);
			if( pDIPlus )	*pDIPlus	=	dDIPlus;
			if( pDIMinus )	*pDIMinus	=	dDIMinus;
			if( pDX )	*pDX	=	dDX;
			return true;
		}
	}
	return false;
}

int CDMI::signal(size_t nIndex, uint32_t * pnCode)
{
	if( pnCode )	*pnCode	=	ITSC_NOTHING;
	/*	PrepareCache( 0, -1, false );

	if( nIndex <= 0 )
	return ITS_NOTHING;

	double	dDIPlus, dDIMinus, dADX, dADXR;
	double	dDIPlusLast, dDIMinusLast, dADXLast, dADXRLast;
	if( !Calculate( &dDIPlusLast, &dDIMinusLast, &dADXLast, &dADXRLast, nIndex-1, false )
	|| !Calculate( &dDIPlus, &dDIMinus, &dADX, &dADXR, nIndex, false ) )
	return ITS_NOTHING;

	if( dDIPlusLast < dDIMinusLast && dDIPlus > dDIMinus )
	{
	if( pnCode )	*pnCode	=	ITSC_GOLDENFORK;
	return m_itsGoldenFork;
	}
	if( dDIMinusLast < dDIPlusLast && dDIMinus > dDIPlus )
	{
	if( pnCode )	*pnCode	=	ITSC_DEADFORK;
	return m_itsDeadFork;
	}
	*/

	return	ITS_NOTHING;
}

bool CDMI::min_max_info(size_t nStart, size_t nEnd, double *pdMin, double *pdMax)
{
	return AfxGetMinMaxInfo4( nStart, nEnd, pdMin, pdMax, this );
}


bool CDMI::calc(double * pDIPlus, double * pDIMinus, double *pADX, double *pADXR, size_t nIndex, bool bUseLast)
{
	STT_ASSERT_CALCULATE1( m_pKData, nIndex );

	if( m_nDays > nIndex )
		return false;

	if( load_from_cache( nIndex, pDIPlus, pDIMinus, pADX, pADXR ) )
		return true;

	double	dDX = 0, dADX = 0, dADXR = 0;
	if( !CalculateDIDX( pDIPlus, pDIMinus, &dDX, nIndex, m_nDays ) )
		return false;

	if( NULL == m_pdDMICache )
	{
		m_pdDMICache		=	new double[m_nDays];
		m_pnDMICacheIndex	=	new	int[m_nDays];
		if( NULL == m_pdDMICache || NULL == m_pnDMICacheIndex )
			return false;
		for( size_t i=0; i<m_nDays; i++ )
			m_pnDMICacheIndex[i]	=	-1;
		m_nDMICacheCurrent	=	-1;
	}

	if( bUseLast && pADX )
	{
		// Calculate ADX and ADXR
		dADX	=	( (m_nDays-1)*(*pADX) + dDX ) / m_nDays;
		for( size_t i=0; i<m_nDays; i++ )
		{
			if( m_pnDMICacheIndex[i] == nIndex-m_nDays )
			{
				dADXR	=	(m_pdDMICache[i] + dADX) / 2;
				break;
			}
		}

		// Save ADX to cache
		m_nDMICacheCurrent	++;
		if( m_nDMICacheCurrent < 0 || m_nDMICacheCurrent >= m_nDays )
			m_nDMICacheCurrent	=	0;
		m_pdDMICache[m_nDMICacheCurrent]	=	dADX;
		m_pnDMICacheIndex[m_nDMICacheCurrent]	=	nIndex;

		if( pADX )	*pADX	=	dADX;
		if( pADXR )	*pADXR	=	dADXR;
		store_to_cache( nIndex, pDIPlus, pDIMinus, pADX, pADXR );
		return true;
	}
	else
	{
		double	factor = 1;
		size_t k;
		for( k=nIndex; k > 0; k-- )
		{
			factor	*=	((double)(m_nDays-1))/m_nDays;
			if( factor < 0.001 )
				break;
		}
		double	dADXLast = 0;
		bool	bHasADXR = false;
		for( ; k<=nIndex; k++ )
		{
			if( !CalculateDIDX( NULL, NULL, &dDX, k, m_nDays ) )
				continue;

			// Calculate ADX and ADXR
			dADX	=	( (m_nDays-1)*dADX + dDX )/m_nDays;
			if( k == nIndex-m_nDays )
			{
				dADXLast	=	dADX;
				bHasADXR	=	true;
			}

			// Save ADX to cache
			m_nDMICacheCurrent	++;
			if( m_nDMICacheCurrent < 0 || m_nDMICacheCurrent >= m_nDays )
				m_nDMICacheCurrent	=	0;
			m_pdDMICache[m_nDMICacheCurrent]	=	dADX;
			m_pnDMICacheIndex[m_nDMICacheCurrent]	=	k;
		}
		dADXR	=	(dADX + dADXLast) / 2;
		if( pADX )				*pADX	=	dADX;
		if( pADXR && bHasADXR )	*pADXR	=	dADXR;
		store_to_cache( nIndex, pDIPlus, pDIMinus, pADX, pADXR );
		return true;
	}
	return false;
}
